package com.lx.boot.web;

import com.lx.annotation.Note;
import com.lx.boot.OS;
import com.lx.boot.cache.util.CacheUtils;
import com.lx.boot.web.config.AuthProperties;
import com.lx.constant.DefaultResult;
import com.lx.entity.JwtInfo;
import com.lx.entity.UserInfo;
import com.lx.util.LX;
import com.lx.util.secure.algorithm.summary.HmacSha;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

import static com.lx.constant.DefaultBaseConstant.ADMIN_PERMISSIONS;
import static com.lx.constant.DefaultBaseConstant.AUTHORIZATION;
import static com.lx.constant.DefaultRedisConstant.RedisPrefix.*;


@Slf4j
@Note("登录信息工具类")
public class AuthUtil {

    //说明: 生成token
    /**{ ylx } 2022/4/1 13:43 */
    @Note("登录时创建token, 参数可为UserInfo的子类")
    public UserInfo createToken(@Valid @NotNull(message = "用户信息不能为空!")UserInfo userInfo){
        // 删除上次token 默认单个地方登录
        deleteUserToken(userInfo);

        AuthProperties authProperties = OS.getBeanProperty(AuthProperties.class);
        long time = authProperties.getTimeoutMinutesByTokenType(userInfo.getTokenType());
        String token = LX.uuid32();
        // 保存用户信息
        CacheUtils.put(USER_LOGIN_INFO_USER_INFO_APPID_BY_TOKEN+token,userInfo,time, TimeUnit.MINUTES );
        // 保存用户的token 方便删除
        Map<String, String> tokenMap = LX.ifEmpty(CacheUtils.get(USER_LOGIN_INFO_TOKEN_BY_USER_ID_USER_TYPE + userInfo.getUserId()), new HashMap<String, String>());
        tokenMap.put(userInfo.getTokenType(),token);
        CacheUtils.put(USER_LOGIN_INFO_TOKEN_BY_USER_ID_USER_TYPE+userInfo.getUserId(),tokenMap,time, TimeUnit.MINUTES );
        userInfo.setToken(token);
        return userInfo;
    }
    @Note("根据userId更新用户权限信息")
    public void updateAllUserPermissionByUserId(String userId, String permissionId, String userType){
        Map<String,String> tokenMap = CacheUtils.get(USER_LOGIN_INFO_TOKEN_BY_USER_ID_USER_TYPE + userId);
        if (LX.isNotEmpty(tokenMap)){
            tokenMap.forEach((tokenType,token)->{
                updateAllUserPermissionByToken(token,permissionId,userType);
            });
        }
    }
    @Note("根据token更新用户权限信息")
    public void updateAllUserPermissionByToken(String token, String permissionId, String userType){
        UserInfo userInfo = getUserInfoByToken(UserInfo.class,token);
        if (userInfo != null){
            userInfo.setPermissionId(permissionId);
            userInfo.setUserType(userType);
            updateUserInfoByToken(token,userInfo);
        }
    }
    @Note("根据token更新用户信息")
    public void updateUserInfoByToken(String token , UserInfo userInfo){
        AuthProperties authProperties = OS.getBeanProperty(AuthProperties.class);
        long time = authProperties.getTimeoutMinutesByTokenType(userInfo.getTokenType());
        CacheUtils.put(USER_LOGIN_INFO_USER_INFO_APPID_BY_TOKEN+token,userInfo,time, TimeUnit.MINUTES );
    }
    @Note("token续期")
    public void renewal(String token , UserInfo userInfo){
        AuthProperties authProperties = OS.getBeanProperty(AuthProperties.class);
        if (authProperties.isRenewal()){
            long time = authProperties.getTimeoutMinutesByTokenType(userInfo.getTokenType());
            CacheUtils.renewal(USER_LOGIN_INFO_USER_INFO_APPID_BY_TOKEN+token,time, TimeUnit.MINUTES);
            CacheUtils.renewal(USER_LOGIN_INFO_TOKEN_BY_USER_ID_USER_TYPE+userInfo.getUserId(),time, TimeUnit.MINUTES);
        }
    }


    //说明: 删除用户登录的token
    /**{ ylx } 2022/4/1 14:04 */
    @Note("删除用户上次登录的token")
    public void deleteUserToken(@Valid @NotNull(message = "用户信息不能为空!") UserInfo userInfo){
        Map<String,String> tokenMap = CacheUtils.get(USER_LOGIN_INFO_TOKEN_BY_USER_ID_USER_TYPE + userInfo.getUserId());
        if (LX.isNotEmpty(tokenMap)){
            CacheUtils.evict(USER_LOGIN_INFO_USER_INFO_APPID_BY_TOKEN+tokenMap.get(userInfo.getTokenType()));
        }
    }
    //说明: 删除用户登录的token
    /**{ ylx } 2022/4/1 14:04 */
    @Note("删除用户所有的token")
    public void deleteUserAllToken(@Valid @NotNull(message = "用户信息不能为空!") UserInfo userInfo){
        Map<String,String> tokenMap = CacheUtils.get(USER_LOGIN_INFO_TOKEN_BY_USER_ID_USER_TYPE + userInfo.getUserId());
        if (LX.isNotEmpty(tokenMap)){
            tokenMap.forEach((k,v)->{
                CacheUtils.evict(USER_LOGIN_INFO_USER_INFO_APPID_BY_TOKEN+v);
            });
        }
        CacheUtils.evict(USER_LOGIN_INFO_TOKEN_BY_USER_ID_USER_TYPE + userInfo.getUserId());
    }


    @Note("获取权限信息 子类重写该方法实现获取权限")
    public Set<String> getPermissions(String permissionId){
        return CacheUtils.get(USER_LOGIN_INFO_PERMISSIONS + permissionId);
    }

    @Note("获取权限信息")
    public Set<String> getPermissions(String permissionId, Callable<Set<String>> valueLoader){
        if(LX.isEmpty(permissionId)){
            return null;
        }
        return CacheUtils.get(USER_LOGIN_INFO_PERMISSIONS + permissionId, valueLoader, 30, TimeUnit.DAYS);
    }

    @Note("删除权限信息")
    public void deletePermissions(String permissionId){
        CacheUtils.evict(USER_LOGIN_INFO_PERMISSIONS+permissionId);
    }

    @Note("判断权限id是否有指定权限")
    public boolean checkUserPermissions(String permission, UserInfo userInfo){
        if (userInfo == null || LX.isEmpty(permission)){
            return false;
        }
        if (userInfo.superAdmin()){//超级管理员
            return true;
        }
        String all = (permission.contains("/")?permission.substring(0,permission.lastIndexOf("/")):permission) +"/*";
        Set<String> permissions = getPermissions(userInfo.getPermissionId());
        if (LX.isNotEmpty(permissions) && (permissions.contains(ADMIN_PERMISSIONS) ||  //超管
                permissions.contains(permission) ||  //包含权限
                permissions.contains(all)) ){ //某个子权限的全部权限
            return true;
        }
        return false;
    }


    //说明: 获取用户信息
    /**{ ylx } 2022/4/1 11:38 */
    @Note("获取用户信息")
    public UserInfo getUserInfo(HttpServletRequest request){
        return getUserInfo(UserInfo.class, request);
    }
    //说明: 获取用户信息
    /**{ ylx } 2022/4/1 11:38 */
    @Note("获取用户信息, T为UserInfo的子类")
    public <T extends UserInfo>T getUserInfo(Class<T> t, HttpServletRequest request){
        return getUserInfo(t,request.getHeader(AUTHORIZATION));
    }
    //说明: 获取用户信息
    /**{ ylx } 2022/4/1 11:38 */
    @Note("获取用户信息, T为UserInfo的子类")
    public UserInfo getUserInfo(String token){
        return getUserInfo(UserInfo.class, token);
    }
    //说明: 获取用户信息
    /**{ ylx } 2022/4/1 11:38 */
    @Note("获取用户信息, T为UserInfo的子类")
    public <T extends UserInfo>T getUserInfo(Class<T> t, String token){
        try {
            if (LX.isEmpty(token)){
                return null;
            }

            T userInfo = getUserInfoByToken(t,token);
            if (userInfo != null){
                renewal(token, userInfo); // 用户信息不为空进行续期
            }
            return userInfo;
        }catch (Exception e){
            log.error("获取用户信息时发生错误!", e);
            throw new RuntimeException("获取用户信息时发生错误!",e);
        }
    }

    @Note("获取用户信息, T为UserInfo的子类")
    private <T extends UserInfo>T getUserInfoByToken(Class<T> t,String token){
        if (LX.isEmpty(token)){
            return null;
        }
        T userInfo = null;
        Object o = CacheUtils.get(USER_LOGIN_INFO_USER_INFO_APPID_BY_TOKEN + token);
        if (o != null){
            if (UserInfo.class.isAssignableFrom(o.getClass())){
                userInfo = (T) o;
            }else{
                userInfo = LX.toObj(t,o);
            }
        }
        return userInfo;
    }

//    private final static String JWT_HEADER = base64UrlEncode("{\"alg\": \"HS256\",\"typ\": \"JWT\"}");
//    @Note("登录时创建JWT token, 参数可为UserInfo的子类  注意:该类型未控制用户在多个位置同时登录")
//    public String createJwtToken(UserInfo userInfo){
//        AuthProperties authProperties = OS.getBeanProperty(AuthProperties.class);
//        JwtInfo jwtInfo = new JwtInfo(userInfo,TimeUnit.MINUTES,authProperties.getTimeoutMinutesByTokenType(userInfo.getTokenType()));
//        String payloadJson = LX.toJSONString(jwtInfo);
//        String payload = base64UrlEncode(payloadJson);
//        byte[] res = HmacSha.hmacSHA("HmacSHA256",authProperties.getJwtKey().getBytes(StandardCharsets.UTF_8),JWT_HEADER.getBytes(StandardCharsets.UTF_8),new byte[]{(byte)46},payload.getBytes(StandardCharsets.UTF_8));
//        return JWT_HEADER+"."+payload+"."+base64UrlEncode(res);
//    }
//
//    @Note("通过jwt获取用户信息")
//    private static UserInfo getJwtUserInfo(String token){
//        if (LX.isEmpty(token)){
//            return null;
//        }
//        String[] split = token.split("\\.");
//        if (split.length != 3){
//            return null;
//        }
//        String header = split[0];
//        String payload = split[1];
//        AuthProperties authProperties = OS.getBeanProperty(AuthProperties.class);
//        byte[] res = HmacSha.hmacSHA("HmacSHA256", authProperties.getJwtKey().getBytes(StandardCharsets.UTF_8),header.getBytes(StandardCharsets.UTF_8),new byte[]{(byte)46},payload.getBytes(StandardCharsets.UTF_8));
//        if (split[2].equals(base64UrlEncode(res))){
//            JwtInfo jwtInfo = LX.toObj(JwtInfo.class, base64UrlDncode(payload));
//            if (!jwtInfo.expired()){
//                return jwtInfo.getUserInfo();
//            }
//        }
//        return null;
//    }
//    @Note("使用无填充的 base64URL 进行编码")
//    private static String base64UrlEncode(byte[] data){
//        return Base64.getUrlEncoder().withoutPadding().encodeToString(data);
//    }
//    private static String base64UrlEncode(String data){
//        return Base64.getUrlEncoder().withoutPadding().encodeToString(data.getBytes(StandardCharsets.UTF_8));
//    }
//    private static String base64UrlDncode(String data){
//        return new String(Base64.getUrlDecoder().decode(data));
//    }
}
